package mytaxis.move.app;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import mytaxis.move.calendar.CalendarActivity;
import mytaxis.move.calendar.PendingRequestsActivity;
import mytaxis.move.model.MyTaxisModel;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;

import mytaxis.move.R;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;

public class MyTaxisService extends Service {

	private MyTaxisApplication app;
	private MyTaxisModel model;
	private final String URL = "http://23.23.184.253/taxi/";
	private final String URL_GOOGLE = "http://maps.googleapis.com/maps/api/geocode/json?latlng=";
	private final String SENSOR_TRUE= "sensor=true";
	private final String LOGIN = "login";
	private final String ESTAT = "estat";
	private final String SOLICITUDS = "solicituds";
	private final String MAIL = "mail";
	private final String MAIL_TAXI = "mail_taxi";
	private final String TOKEN = "token";
	private final String ID = "id";
	private final String PASS = "pass";
	private final String POSX = "posX";
	private final String POSY = "posY";
	private final String SOLICITUD = "solicitud";
	public static final String IMM_REQ_FILTER = "mytaxis.move.filter.immsol";
	public static final String IMM_REQ_DATA = "mytaxis.move.filter.immsol";
	private final IBinder mBinder = new LocalBinder();
	private static final int TIME_ENV_POS = 1000;
	private static final int TIME_CHECK_REQUESTS = 30000;
	private static final int TIME_DELETE = 60000;
	private int authToken = -1;
	private boolean isAvailable = false;
	private int idSol = -1;
	private int idSolTemp = -1;
	private String mail;
	private LocationManager locManager;
	private Location myloc;
	private LocationListener locListener;
	private Thread env_pos;
	private Thread check_estat;
	private Thread check_requests;
	private Thread delete_pending;
	
	@Override
	public IBinder onBind(Intent arg0) {
		return mBinder;
	}
	
	@Override
	public boolean onUnbind(Intent intent) {
	    return true;
	}
	
	@Override
    public void onCreate() {
          super.onCreate();
          
          app = (MyTaxisApplication) getApplication();
          model = new MyTaxisModel(this);
          initMyLocation();
          
          env_pos = new Thread() {		// THREAD ENV POS
          	public void run(){
          		while(true){
          			if(authToken > 0 && isAvailable){
          				try {
          					if(myloc!=null) sendPosServer();
						} catch (JSONException e) {
							e.printStackTrace();
						}
          			}
	            	try {
						Thread.sleep(TIME_ENV_POS);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
          		}
          	}
          };
          check_estat = new Thread() {		// THREAD ENV POS
            	public void run(){
            		while(true){
            			if(authToken > 0){
            				try {
  								checkEstat();
	  						} catch (JSONException e) {
	  							e.printStackTrace();
	  						}
            			}
  	            	try {
  						Thread.sleep(TIME_ENV_POS);
  					} catch (InterruptedException e) {
  						e.printStackTrace();
  					}
            		}
            	}
            };
          check_requests = new Thread() {		// THREAD CHECK REQUESTS
          	public void run(){
          		while(true){
        			if(authToken > 0){
        				try {
							checkRequests();
  						} catch (JSONException e) {
  							e.printStackTrace();
  						}
        			}
        			try {
  						Thread.sleep(TIME_CHECK_REQUESTS);
  					} catch (InterruptedException e) {
  						e.printStackTrace();
  					}
          		}
          	}
          };
          delete_pending  = new Thread() {		// THREAD CHECK REQUESTS
            	public void run(){
              		while(true){
            			if(authToken > 0){
            				try {
    							checkPendingRequests();
      						} catch (Exception e) {
      							e.printStackTrace();
      						}
            			}
            			try {
      						Thread.sleep(TIME_DELETE);
      					} catch (InterruptedException e) {
      						e.printStackTrace();
      					}
              		}
              	}
              };
          env_pos.start();    
          check_estat.start();
          check_requests.start();
          delete_pending.start();
	}
	
	private void initMyLocation() {
		locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		myloc = locManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

		locListener = new LocationListener() {
			public void onLocationChanged(Location location) {
				myloc = location;
			}
			public void onProviderDisabled(String provider) {

			}
			public void onProviderEnabled(String provider) {

			}
			public void onStatusChanged(String provider, int status,
					Bundle extras) {

			}
		};

		locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 30000,
				0, locListener);
	}
	
	public void sendPosServer() throws JSONException{
		HttpClient httpClient = new DefaultHttpClient();
		HttpPost post = new HttpPost(URL+ESTAT+ "/" + mail);

		try
		{	
			List<NameValuePair> l = new ArrayList<NameValuePair>(3);
			l.add(new BasicNameValuePair(POSX, String.valueOf(myloc.getLongitude())));
			l.add(new BasicNameValuePair(POSY, String.valueOf(myloc.getLatitude())));
			l.add(new BasicNameValuePair(TOKEN, String.valueOf(authToken)));
			
			post.setEntity(new UrlEncodedFormEntity(l));

			httpClient.execute(post);
		}
		catch(Exception ex)
		{
			Log.e("ServicioRest","Error!", ex);
		}

	}
	
	public void checkEstat() throws JSONException{
		HttpClient httpClient = new DefaultHttpClient();
		HttpGet get = new HttpGet(URL+ESTAT+ "/" + MAIL+ "/" +mail+ "/" + TOKEN + "/" + authToken);
		
		try{
			HttpResponse resp = httpClient.execute(get);
			String respStr = EntityUtils.toString(resp.getEntity());
			String[] s = respStr.substring(2, respStr.length()-2).split(",");
			int id_solicitud = Integer.parseInt(s[0].split(":")[1]);
			if(idSol == -1 && id_solicitud >=0){
				idSolTemp=id_solicitud;
				if(!isAvailable) acceptRequest(false,"-1");
				else{				
					Intent intent = new Intent();
					intent.putExtra(IMM_REQ_DATA, "1");
					intent.setAction(IMM_REQ_FILTER); //Define intent-filter
					sendBroadcast(intent);
				}
			}else if(idSol !=-1 && id_solicitud ==-1){
				Intent intent = new Intent();
				intent.putExtra(IMM_REQ_DATA, "0");
				intent.setAction(IMM_REQ_FILTER); //Define intent-filter
				sendBroadcast(intent);
				idSol=-1;
				idSolTemp = -1;
				app.setHasRequest(false);
			}
		}
		catch(Exception ex)
		{
			Log.e("ServicioRest","Error!", ex);
		}

	}
	
	public void checkRequests() throws JSONException{
		HttpClient httpClient = new DefaultHttpClient();
		HttpGet get = new HttpGet(URL+SOLICITUDS+ "/" + MAIL+ "/" +mail+ "/" + TOKEN + "/" + authToken);
		
		try{
			HttpResponse resp = httpClient.execute(get);
			String respStr = EntityUtils.toString(resp.getEntity());
			if(!respStr.equals("-1") && !respStr.equals("-2") && !respStr.equals("-3") && 
					!respStr.equals("-4") && !respStr.equals("-9")){
				String[] s = respStr.substring(2, respStr.length()-2).split("\\},\\{");
				String ids[] = new String[s.length];
				boolean newTask = false;
				for(int i=0; i<s.length; i++){
					String[] params = s[i].split(",");
					if(!params[3].split(":")[1].equals("null")){
						String id = params[0].split(":")[1];
						ids[i] = id;
						if(!model.taskExists(id)){
							String url = URL_GOOGLE+params[2].split(":")[1]+","+params[1].split(":")[1]+"&"+SENSOR_TRUE;
							InputStream is = getConnection(url);
							String res = convertToString(is);
							String address = res.split("\"formatted_address\" : ")[1].split("\"")[1];
							model.insertPendingRequest(id, params[1].split(":")[1], params[2].split(":")[1],
									params[3].split(":")[1], params[4].split(":")[1]+":"+params[4].split(":")[2],address);
							newTask = true;
						}
					}else ids[i] = "-1";
				}
				if(newTask) showAppNotification("Request received","Click here to see the requests list.");
				checkCanceledByUser(ids);	
			}
			else checkCanceledByUser(null);
			
		}
		catch(Exception ex)
		{
			Log.e("ServicioRest","Error!", ex);
		}

	}
	
	private void checkCanceledByUser(String[] ids) {
		Cursor c = model.getTasksWithAnotherId(ids);
		boolean taskCanceled = false;
		if(c.moveToFirst()){
			do{
				taskCanceled = true;
				model.updateStatus(c.getString(0), getResources().getStringArray(R.array.status)[3]);
			}while(c.moveToNext());
		}
		c.close();
		if(taskCanceled) showAppNotification("Request canceled by client","Click here to see the calendar.");
		c = model.getPendingTasksWithAnotherId(ids);
		if(c.moveToFirst()){
			do{
				model.deletePendingRequest(c.getString(0));
			}while(c.moveToNext());
		}
		c.close();
	}

	private InputStream getConnection(String url) {
		InputStream is = null;
		try {
			URLConnection conn = new URL(url).openConnection();
			is = conn.getInputStream();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return is;
	}
	
	private String convertToString (InputStream is) throws IOException {
		if (is != null) {
			Writer writer = new StringWriter();
			char[] buffer = new char[1024];
			try {
				Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
				int n;
				while ((n = reader.read(buffer)) != -1) {		
					writer.write(buffer, 0, n);		
				}		
			} finally {
				is.close();
			}
			return writer.toString();
		} else {       
			return "";
		}
	}
	
	private void showAppNotification(String title, String content) {
		NotificationManager mManager;
		mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

		Notification notification = new Notification(R.drawable.taxi_app_icon,
				"MyTaxis", System.currentTimeMillis());
		Intent notificationIntent;
		if(title.equals("Request received")) notificationIntent = new Intent(this,PendingRequestsActivity.class);
		else notificationIntent = new Intent(this,CalendarActivity.class);
		PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
				notificationIntent, 0);

		notification.setLatestEventInfo(this, title, content, contentIntent);
		notification.flags = Notification.FLAG_AUTO_CANCEL;
		notification.defaults |= Notification.DEFAULT_SOUND;

		mManager.notify(1, notification);

	}
	
	private void checkPendingRequests(){
		Cursor c = model.getIdTimePendings();
		if(c.moveToFirst()){
			do{
				long now = model.getNowTime();
				if(c.getLong(1)+3600 < now){
					if(acceptRequest(false,c.getString(0))){
						model.deletePendingRequest(c.getString(0));
					}
				}
			}while(c.moveToNext());
		}
		c.close();
	}
	
	public boolean acceptRequest(boolean b, String id) {
		HttpClient httpClient = new DefaultHttpClient();
		HttpPost post;
		if(id.equals("-1")) post = new HttpPost(URL+SOLICITUDS+ "/" + idSolTemp);
		else post = new HttpPost(URL+SOLICITUDS+ "/" + id);
		try
		{	
			List<NameValuePair> l = new ArrayList<NameValuePair>(3);
			if(b) l.add(new BasicNameValuePair(SOLICITUD, "1"));
			else l.add(new BasicNameValuePair(SOLICITUD, "0"));
			l.add(new BasicNameValuePair(MAIL_TAXI, mail));
			l.add(new BasicNameValuePair(TOKEN, String.valueOf(authToken)));
			
			post.setEntity(new UrlEncodedFormEntity(l));

			HttpResponse resp = httpClient.execute(post);
			String respStr = EntityUtils.toString(resp.getEntity());
			if(id.equals("-1")){
				if(respStr.equals("0") && !b){
					idSolTemp = -1;
					idSol = -1;
					app.setHasRequest(false);
				}
				else if(respStr.equals("0") && b){
					idSol = idSolTemp;
					app.setHasRequest(true);
				}
			}
			
			return respStr.equals("0");
		}
		catch(Exception ex)
		{
			Log.e("ServicioRest","Error!", ex);
		}
		return false;
	}
	
	public Bundle getRequestData() {
		HttpClient httpClient = new DefaultHttpClient();
		HttpGet get = new HttpGet(URL+SOLICITUDS+"/"+ID+"/"+idSol+"/"+MAIL+"/"+mail+"/"+TOKEN+"/"+authToken);
		try
		{	
			HttpResponse resp = httpClient.execute(get);
			String respStr = EntityUtils.toString(resp.getEntity());
			String[] s = respStr.substring(2, respStr.length()-2).split(",");
			double posX = Double.parseDouble(s[0].split(":")[1]);
			double posY = Double.parseDouble(s[1].split(":")[1]);
			
			Bundle b = new Bundle();
			b.putDouble("posX", posX); 
			b.putDouble("posY", posY); 
			return b;
		}
		catch(Exception ex)
		{
			Log.e("ServicioRest","Error!", ex);
		}
		return null;
	}
	
	public boolean hasActiveRequest() {
		return idSol != -1;
	}

	public boolean endRequest(String id) {
		HttpClient httpClient = new DefaultHttpClient();
		HttpDelete delete;
		if(id.equals("-1")) delete = new HttpDelete(URL+SOLICITUDS+"/"+ID+"/"+idSol+"/"+MAIL+"/"+mail+"/"+TOKEN+"/"+authToken);
		else delete = new HttpDelete(URL+SOLICITUDS+"/"+ID+"/"+id+"/"+MAIL+"/"+mail+"/"+TOKEN+"/"+authToken);
		try
		{	
			HttpResponse resp = httpClient.execute(delete);
			String respStr = EntityUtils.toString(resp.getEntity());
			
			if(respStr.equals("0")) app.setHasRequest(false);
			
			return respStr.equals("0");
		}
		catch(Exception ex)
		{
			Log.e("ServicioRest","Error!", ex);
		}
		return false;
	}
	
	
	
	public class LocalBinder extends Binder {
        public MyTaxisService getService() {
            return MyTaxisService.this;
        }
	}
	
	public void setAvailable(boolean b){
		isAvailable = b;
	}
	
	public boolean isAvailable() {
		return isAvailable;
	}

	public boolean login(String mail, String pass){
		boolean ok = false;
		HttpClient httpClient = new DefaultHttpClient();
		HttpPost post = new HttpPost(URL+LOGIN+"/"+ mail);

		try
		{	
			List<NameValuePair> l = new ArrayList<NameValuePair>(1);
			l.add(new BasicNameValuePair(PASS, pass));
			
			post.setEntity(new UrlEncodedFormEntity(l));

			HttpResponse resp = httpClient.execute(post);
			String respStr = EntityUtils.toString(resp.getEntity());

			if(Integer.parseInt(respStr)>0){
				ok = true;
				authToken = Integer.parseInt(respStr);
				app.setAuthToken(authToken);
				this.mail = mail;
			}
		}
		catch(Exception ex)
		{
			Log.e("ServicioRest","Error!", ex);
		}
		return ok;
	}

	public boolean logout() {
		boolean ok = false;
		HttpClient httpClient = new DefaultHttpClient();
		HttpPost post = new HttpPost(URL+ESTAT+"/"+ mail);
		
		try
		{	
			List<NameValuePair> l = new ArrayList<NameValuePair>(2);
			l.add(new BasicNameValuePair("logout", "1"));
			l.add(new BasicNameValuePair(TOKEN, String.valueOf(authToken)));
			
			post.setEntity(new UrlEncodedFormEntity(l));

			HttpResponse resp = httpClient.execute(post);
			String respStr = EntityUtils.toString(resp.getEntity());

			if(respStr.equals("0")){
				ok = true;
				authToken = -1;
				app.setAuthToken(authToken);
			}
		}
		catch(Exception ex)
		{
			Log.e("ServicioRest","Error!", ex);
		}
		return ok;
	}
}
